Introduction to data masking with pyActigraphy

This introduction notebook illustrates how to mask potentially spurious inactivity periods.

First, load the pyActigraphy package:

In [1]:
import pyActigraphy
/usr/local/lib/python3.7/site-packages/statsmodels/tools/_testing.py:19: FutureWarning: pandas.util.testing is deprecated. Use the functions in the public API at pandas.testing instead.
  import pandas.util.testing as tm

As well as your favourite plotting package:

In [2]:
import plotly.graph_objs as go

Read an individual actigraphy file

As an example, let’s read a .awd file located in the test directory of the pyActigraphy package:

In [3]:
import os
fpath = os.path.join(os.path.dirname(pyActigraphy.__file__),'tests/data/')
In [4]:
raw = pyActigraphy.io.read_raw_awd(fpath+'example_01_mask.AWD')

Plot the data

In [5]:
layout = go.Layout(title="Actigraphy data", xaxis=dict(title="Date time"), yaxis=dict(title="Counts/period"), showlegend=False)
In [6]:
go.Figure(data=go.Scatter(x=raw.data.index.astype(str), y=raw.data), layout=layout)

At first sight, it seems that there is a suspicious period of total inactivity (activity counts equal to zero), starting at 9h30 on the 27th of January.

It seems spurious for several reasons:

  • it happens outside the usually sleep time window (although a nap might be possible…);

  • it consists of a long series of consecutive “total” inactivity (i.e. zero count) while other potential sleep periods do not;

  • it exhibits a sharp decrease of activity with respect to the surrounding time periods;

Most likely, it corresponds to a period where the actigraph has been removed by the participant.

Mask inactive data

While it is fairly (although not totally) simple to deal with inactivity periods at the beginning and at the end of the recordings (cf this other tutorial notebook), it is more difficult to deal, in an automatic fashion, with inactivity periods happening during the recording.

Fortunately, the pyActigraphy package offers a simple way to deal with these periods by masking the corresponding data.

Definition

Spurious inactivity periods are defined as series of consecutive zeros. The lenght of these series is configurable.

Create a mask

In [7]:
raw.frequency
Out[7]:
Timedelta('0 days 00:01:00')
In [8]:
# The duration corresponds either to the minimal number of inactive epochs (ex: duration=120)
# or the minimal length of the inactive period (duration='2h00min')
raw.create_inactivity_mask(duration='2h00min')
In [9]:
raw.inactivity_length
Out[9]:
120

WARNING: Creating a mask does not mean it is applied. Cf. next section.

Before applying it, let’s visualize it:

In [10]:
layout = go.Layout(title="Data mask", xaxis=dict(title="Date time"), yaxis=dict(title="Mask"), showlegend=False)
In [11]:
go.Figure(data=go.Scatter(x=raw.mask.index.astype(str),y=raw.mask),layout=layout)

Periods in the data for which the mask is equal to zero will be masked (once the mask is applied).

As expected, the spurious period of total inactivity of the 27th of January has been found by the pyActigraphy package and therefore can be masked upon request (cf. next section).

Once a mask has been created, it is possible to dynamically change the configuration of its minimal duration:

In [12]:
raw.inactivity_length = '4h'
In [13]:
go.Figure(data=go.Scatter(x=raw.mask.index.astype(str),y=raw.mask),layout=layout)

Apply the mask

Masking the spurious inactivity periods is crucial as it can substantially affect the calculation of the rest-activity rhythm related variables.

For example:

Applying the mask is controlled by the following variable:

In [14]:
raw.mask_inactivity = True
In [15]:
raw.IS()
Out[15]:
0.5754713250134655

Interesting, isn’t it?

Besides, the application of the masking is dynamic:

In [16]:
raw.mask_inactivity = False
In [17]:
raw.IS()
Out[17]:
0.4453375595033997

A last word; be cautious when masking your data. Series of consecutive periods of total inactivity might happen during sleep… A visual check of the mask is always a good idea.